package com.tbit.main.controller;

import cn.hutool.core.collection.CollUtil;
import com.tbit.common.constant.MethodEnum;
import com.tbit.common.constant.ModuleEnum;
import com.tbit.main.common.JsonResult;
import com.tbit.main.constant.MapConstant;
import com.tbit.main.constant.OperateLog;
import com.tbit.main.constant.*;
import com.tbit.main.constant.enums.ParkPointType;
import com.tbit.main.exception.BaseException;
import com.tbit.main.h2.pojo.ParkPoint;
import com.tbit.main.pojo.*;
import com.tbit.main.service.*;
import com.tbit.main.util.Assert;
import com.tbit.main.util.GeoUtil;
import com.tbit.main.util.ObjectUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 停车点
 *
 * @author Leon
 * 2017年2月24日 下午5:51:05
 */
@RestController
@RequestMapping("/parkPoint")
public class ParkPointController {
    @Autowired
    private ParkPointService parkPointService;
    @Autowired
    private GeoService geoService;
    @Autowired
    private RideLogService rideLogService;
    @Autowired
    private CalibrateService calibrateService;
    @Autowired
    private TokenService tokenService;
    @Autowired
    private GeoreGionService georeGionService;
    @Autowired
    private DataService dataService;
    @Autowired
    private ReturnBikeMsgLogService returnBikeMsgLogService;


    /**
     * 添加
     */
    @RequestMapping("/add")
    @OperateLog(module = ModuleEnum.MAINPARKPOINT, methods = MethodEnum.PARKPOINT_ADD)
    public JsonResult add(String token, ParkPoint parkPoint, Integer mapType) {

        /* 校验 */
        Assert.notNull(parkPoint.getParkPointType(), "ParkPoint.type.notNull");
        Assert.hasLength(parkPoint.getName(), "ParkPoint.name.notNull");
        Assert.notNull(parkPoint.getAccountId(), "AccountUser.agentId.notNull");
        if (mapType == null) {
            mapType = MapConstant.MAP_GOOGLE;
        }
        //兼容ios不能传null
        if (parkPoint.getAllowRange() == null || parkPoint.getAllowRange() == -1){
            parkPoint.setAllowRange(-1);
        }

        // 校验站点是否已存在&是否重名
        List<ParkPoint> DB = parkPointService.getAccountIdName(parkPoint.getAccountId(), parkPoint.getName());
        if (DB != null && !DB.isEmpty()) {
            throw new BaseException("parkpoint.name.exit");
        }

        // 校验多边形面积是否小于最小值
        List<Point> pointList = GeoUtil.getPoints(parkPoint.getPoints());
        if (GeoUtil.pointArea(pointList) < 10) {
            throw new BaseException("站点面积过小（小于10平方米），可能会导致还车失败，请适当放大。");
        }

        if (ParkPointType.PARKPOINT_CIRCULAR_TYPE.getType().equals(parkPoint.getParkPointType())) {// 圆形
            Assert.notNull(parkPoint.getLo(), "Map.lon.nutNull");
            Assert.notNull(parkPoint.getLa(), "Map.lat.nutNull");

            Point point = new Point(parkPoint.getLo(), parkPoint.getLa());
            /** 保存高德地图原始经纬度 */
            parkPoint.setLaC(point.getY());
            parkPoint.setLoC(point.getX());
        } else if (ParkPointType.PARKPOINT_POLYGON_TYPE.getType().equals(parkPoint.getParkPointType())) {// 多边形
            Assert.notNull(parkPoint.getPoints(), "ParkPoint.points.notNull");

            /** 计算电子围栏中心点 */
            List<Point> points = GeoUtil.getPoints(parkPoint.getPoints());
            Point newPoint = GeoUtil.GetCenterPoint(points);

            parkPoint.setLa(newPoint.getY());
            parkPoint.setLo(newPoint.getX());

            /** 保存高德地图原始经纬度 */
            parkPoint.setLaC(newPoint.getY());
            parkPoint.setLoC(newPoint.getX());
            parkPoint.setPointsC(parkPoint.getPoints());
        }
        /** 经纬度校准 */
        calibrateService.calParkPoint(parkPoint, mapType, SysConstant.ORIGINAL);
        /* 备注 */
        if (parkPoint.getRemark() == null) {
            parkPoint.setRemark("");
        }
        if (parkPoint.getSiteType() == null) {
            parkPoint.setSiteType(0);
        }
        parkPointService.insert(parkPoint);

        allocateParkPointPartition(parkPoint);

        return JsonResult.succ();
    }

    /**
     * 划拨站点分区
     */
    private void allocateParkPointPartition(ParkPoint parkPoint) {
        if (CollUtil.isNotEmpty(dataService.getAccountIdGeore(parkPoint.getAccountId()))) {
            /**判断站点分区*/
            List<Map<String, Object>> parkPointGeo = new LinkedList<>();
            boolean exist = true;
            Point point = new Point();
            point.setX(parkPoint.getLo());
            point.setY(parkPoint.getLa());
            List<Integer> regionIdList = dataService.getAccountIdGeore(parkPoint.getAccountId());
            if (CollUtil.isNotEmpty(regionIdList)) {
                for (Integer regionId : regionIdList) {
                    /**在区域内的*/
                    if (CollUtil.isNotEmpty(dataService.getGeore(regionId))) {
                        break;
                    }
                    if (GeoUtil.IsPtInPoly(point, dataService.getGeore(regionId))) {
                        exist = false;
                        if (parkPoint.getRegionId() == null || !parkPoint.getRegionId().equals(regionId)) {
                            Map<String, Object> map = new HashMap<>();
                            map.put("parkPointId", parkPoint.getParkPointId());
                            map.put("regionId", regionId);
                            parkPointGeo.add(map);
                        }
                        break;
                    }
                }
            }

            /**不在所有分区内*/
            if (exist) {
                Map<Double, Integer> minDistance = new TreeMap<>(Double::compareTo);

                for (Integer regionId : dataService.getAccountIdGeore(parkPoint.getAccountId())) {
                    /**与分区的距离*/
                    minDistance.put(GeoUtil.getDistance(point, dataService.getGeore(regionId)), regionId);
                }

                if (parkPoint.getRegionId() == null || !parkPoint.getRegionId().equals(minDistance.values().stream().findFirst().orElse(0))) {
                    Map<String, Object> map = new HashMap<>();
                    map.put("parkPointId", parkPoint.getParkPointId());
                    map.put("regionId", minDistance.values().stream().findFirst().orElse(0));
                    parkPointGeo.add(map);
                }
            }

            if (!parkPointGeo.isEmpty()) {
                parkPointService.updateBatch(parkPointGeo);
            }
        }
    }

    /**
     * 更新
     */
    @RequestMapping("/edit")
    @OperateLog(module = ModuleEnum.MAINPARKPOINT, methods = MethodEnum.PARKPOINT_EDIT)
    public JsonResult edit(String token, ParkPoint parkPoint, Integer mapType) {
        /* 校验 */
        Assert.notNull(parkPoint.getParkPointId(), "ParkPoint.id.notNull");
        Assert.notNull(parkPoint.getAccountId(), "AccountUser.agentId.notNull");
        Assert.notNull(parkPoint.getName(), "ParkPoint.name.notNull");

        if (mapType == null) {
            mapType = MapConstant.MAP_GOOGLE;
        }


        List<ParkPoint> DB = parkPointService.getAccountIdName(parkPoint.getAccountId(), parkPoint.getName());
        if (DB != null && !DB.isEmpty()) {
            if (DB.size() == 1 && DB.get(0).getParkPointId().equals(parkPoint.getParkPointId())) {
            } else {
                throw new BaseException("parkpoint.name.exit");
            }
        }


        if (ParkPointType.PARKPOINT_CIRCULAR_TYPE.getType().equals(parkPoint.getParkPointType())) {//圆形
            Assert.notNull(parkPoint.getLo(), "Map.lon.nutNull");
            Assert.notNull(parkPoint.getLa(), "Map.lat.nutNull");

            Point point = new Point(parkPoint.getLo(), parkPoint.getLa());

            parkPoint.setLa(point.getY());
            parkPoint.setLo(point.getX());
            /** 保存高德地图原始经纬度 */
            parkPoint.setLaC(point.getY());
            parkPoint.setLoC(point.getX());
        } else if (ParkPointType.PARKPOINT_POLYGON_TYPE.getType().equals(parkPoint.getParkPointType())) {//多边形
            Assert.notNull(parkPoint.getPoints(), "ParkPoint.points.notNull");

            /** 计算电子围栏中心点 */
            List<Point> points = GeoUtil.getPoints(parkPoint.getPoints());
            Point newPoint = GeoUtil.GetCenterPoint(points);

            parkPoint.setLa(newPoint.getY());
            parkPoint.setLo(newPoint.getX());
            /** 保存高德地图原始经纬度 */
            parkPoint.setPointsC(parkPoint.getPoints());
        }
        /** 经纬度校准 */
        calibrateService.calParkPoint(parkPoint, mapType, SysConstant.ORIGINAL);

        /**查询停车点*/
        ParkPoint parkPointDB = parkPointService.getByParkPointId(parkPoint.getParkPointId());

        /**替换可修改字段*/
        ObjectUtil.assign(parkPoint, parkPointDB, null);

        parkPointService.update(parkPoint);

        return JsonResult.succ();
    }

    /**
     * 删除
     */
    @RequestMapping("/del")
    @OperateLog(module = ModuleEnum.MAINPARKPOINT, methods = MethodEnum.PARKPOINT_DEL)
    public JsonResult del(String token, Integer parkPointId) {
        /* 校验 */
        Assert.notNull(parkPointId, "ParkPoint.id.notNull");

        /* 权限判断 */
        parkPointService.del(parkPointId);

        return JsonResult.succ();
    }

    /**
     * 根据平台账号id查询
     */
    @RequestMapping("/getByAccountId")
    public JsonResult getByAccountId(String token, Integer mapType, Integer accountId,
                                     @RequestParam(value = "regionId", required = false) List<Integer> regionId,
                                     @RequestParam(value = "level", required = false) Integer level) {
        AccountUser accountUserToken = tokenService.getAccount(token);
        Assert.notNull(accountId, "AccountUser.agentId.notNull");
        if (mapType == null) {
            mapType = SysConstant.GOOGLE;
        }

        if (CollUtil.isEmpty(regionId)) {
            regionId = georeGionService.getByAccountUserId(accountUserToken.getAccountUserId());
        }

        List<ParkPoint> parkPoints;
        if (CollUtil.isNotEmpty(regionId)) {
            parkPoints = parkPointService.getByRegionIdAndLevel(regionId, level);
        } else {
            parkPoints = parkPointService.getByAccountIdAndLevel(accountId, level);
        }

        /**统计站点投放车辆数量*/
        if (!parkPoints.isEmpty()) {
            List<ParkPointMachineCount> ppmcs = parkPointService.groupByParkPoint(accountId);
            Map<Integer, Integer> ppcMap = new HashMap<Integer, Integer>();
            for (ParkPointMachineCount ppmc : ppmcs) {
                ppcMap.put(ppmc.getParkPointId(), ppmc.getMachineCount());
            }

            for (ParkPoint pp : parkPoints) {
                Integer machineCount = ppcMap.get(pp.getParkPointId());
                if (machineCount != null) {
                    pp.setMachineCount(machineCount);
                } else {
                    pp.setMachineCount(0);
                }
            }
        }

        /** 校准 */
        calibrateService.celibrateParkPoint(parkPoints, mapType);

        return JsonResult.succ(parkPoints);
    }

    /**
     * 获取附近站点，经纬度附近
     */
    @RequestMapping("/getNear")
    public JsonResult getNear(String token, Integer mapType, Integer accountId, Double lo, Double la,
                              @RequestParam(value = "regionId", required = false) List<Integer> regionId) {
        AccountUser accountUserToken = tokenService.getAccount(token);

        Assert.notNull(lo, "-2001", "Map.lon.nutNull");
        Assert.notNull(la, "-2002", "Map.lat.nutNull");
        Assert.notNull(accountId, "AccountUser.agentId.notNull");
        if (mapType == null) {
            mapType = SysConstant.GOOGLE;
        }


        Point point = new Point(lo, la);
        point = calibrateService.commonCalibrate(point, mapType, MapConstant.MAP_ORI);

        if (CollUtil.isEmpty(regionId)) {
            regionId = georeGionService.getByAccountUserId(accountUserToken.getAccountUserId());
        }

        List<ParkPoint> parkPoints = parkPointService.getNear(accountId, point, mapType, regionId);

        /** 校准 */
        calibrateService.celibrateParkPoint(parkPoints, mapType);

        return JsonResult.succ(parkPoints);
    }


    /**
     * 获取附近站点，投放时间 站点热力图
     */
    @RequestMapping("/statHeatMap")
    public JsonResult statHeatMap(String token, Integer mapType, Integer accountId, Integer type, String startTime, String endTime) {
        Assert.notNull(accountId, "AccountUser.agentId.notNull");
        Assert.hasLength(startTime, "System.startTime.notNull");
        Assert.hasLength(endTime, "System.endTime.notNull");

        if (mapType == null) {
            mapType = SysConstant.GOOGLE;
        }

        if (type == null) {
            type = 1;
        }

        List<HeatMapStat> list = parkPointService.statByPark(accountId, type, startTime, endTime);
        Geo geo = null;
        List<Geo> geos = geoService.getByAccountId(accountId);
        if (geos.size() > 0) {
            geo = geos.get(0);
        }

        if (list.size() > 0) {
            /**经纬度校准*/
            for (HeatMapStat heat : list) {
                Point point = calibrateService.commonCalibrate(new Point(heat.getLng(), heat.getLat()), MapConstant.MAP_ORI, mapType);
                heat.setLng(point.getX());
                heat.setLat(point.getY());
            }
        }
        if (geo != null) {
            HeatMapStat heatMapStat = new HeatMapStat();
            Point point = calibrateService.commonCalibrate(new Point(Double.parseDouble(geo.getCenterLo()), Double.parseDouble(geo.getCenterLa())), MapConstant.MAP_ORI, mapType);
            heatMapStat.setCount(-1);
            heatMapStat.setLng(point.getX());
            heatMapStat.setLat(point.getY());
            list.add(0, heatMapStat);
        }

        return JsonResult.succ(list);
    }

    /**
     * 分页查询
     */
    @RequestMapping("/statByPark")
    public JsonResult statByPark(String token, Integer accountId, Integer type, String startTime, String endTime, Integer rowCount, Integer pageNO) {
        Assert.notNull(accountId, "AccountUser.agentId.notNull");
        Assert.hasLength(startTime, "System.startTime.notNull");
        Assert.hasLength(endTime, "System.endTime.notNull");

        if (rowCount == null) {
            rowCount = 20;
        }

        if (pageNO == null) {
            pageNO = 1;
        }

        Integer parkCount = rideLogService.statByParkCount(accountId, type, startTime, endTime);

        List<StatPark> statParks = new ArrayList<StatPark>();
        if (parkCount > 0) {
            statParks = rideLogService.statByPark(accountId, type, startTime, endTime, pageNO, rowCount);
        }

        return JsonResult.succ(parkCount, statParks);
    }

    /**
     * 测试
     *
     * @param token
     * @param parkPointId
     * @param mapType
     * @return
     */
    @RequestMapping("/dispatch")
    public JsonResult dispatch(String token, Integer parkPointId, Integer mapType) {
        /* 校验 */
        Assert.notNull(parkPointId, "ParkPoint.id.notNull");

        if (mapType == null) {
            mapType = MapConstant.MAP_GOOGLE;
        }
        return null;
    }

    /**
     * 获取附近站点，根据经纬度，半径
     */
    @RequestMapping("/getNearRadius")
    public JsonResult getNearRadius(String token, Integer mapType, Integer accountId, Double lo, Double la, Integer radius,
                                    @RequestParam(value = "regionId", required = false) List<Integer> regionId) {
        AccountUser accountUserToken = tokenService.getAccount(token);

        Assert.notNull(lo, "-2001", "Map.lon.nutNull");
        Assert.notNull(la, "-2002", "Map.lat.nutNull");
        Assert.notNull(accountId, "AccountUser.agentId.notNull");
        /**默认谷歌地图*/
        if (mapType == null) {
            mapType = SysConstant.GOOGLE;
        }

        /**默认1公里*/
        if (radius == null) {
            radius = 1000;
        }


        Point point = new Point(lo, la);
        point = calibrateService.commonCalibrate(point, mapType, MapConstant.MAP_ORI);

        if (CollUtil.isEmpty(regionId)) {
            regionId = georeGionService.getByAccountUserId(accountUserToken.getAccountUserId());
        }

        List<ParkPoint> prohibitAreas = new LinkedList<>();
        if (CollUtil.isEmpty(regionId)) {
            prohibitAreas = parkPointService.getByAccountAndArea(accountId, point.getX(), point.getY(), radius);
        } else {
            prohibitAreas = parkPointService.getByRegionIdAndArea(regionId, point.getX(), point.getY(), radius);
        }
        /**站点的车辆投放数*/
        this.setParkMachineCount(prohibitAreas,accountId);

        /** 校准 */
        calibrateService.celibrateParkPoint(prohibitAreas, mapType);

        return JsonResult.succ(prohibitAreas);
    }

    /**
     * 根据站点id查询
     *
     * @param token
     * @param parkPointId
     * @param mapType
     * @return
     */
    @RequestMapping("/getByParkPointId")
    public JsonResult getByParkPointId(String token, Integer parkPointId, Integer mapType) {

        ParkPoint parkPoint = parkPointService.getByParkPointId(parkPointId);

        /**默认谷歌地图*/
        if (mapType == null) {
            mapType = SysConstant.GOOGLE;
        }

        /** 校准 */
        calibrateService.celibrateParkPointOne(parkPoint, mapType);

        return JsonResult.succ(parkPoint);
    }


    /**
     * 根据站点名称模糊查询
     *
     * @param token
     * @param accountId
     * @param parkName
     * @param mapType
     * @return
     */
    @RequestMapping("/getByName")
    public JsonResult<List<ParkPoint>> getByName(String token, Integer accountId, String parkName, Integer mapType) {

        List<ParkPoint> listParkPoint = parkPointService.getAccountIdName(accountId, "%" + parkName + "%");

        /**统计站点投放车辆数量*/
        if (listParkPoint.size() > 0) {
            List<Integer> parkpointIds = listParkPoint.stream().map(ParkPoint::getParkPointId).collect(Collectors.toList());
            List<ParkPointMachineCount> ppmcs = parkPointService.groupByParkPoints(parkpointIds);
            Map<Integer, Integer> ppcMap = new HashMap<Integer, Integer>();
            for (ParkPointMachineCount ppmc : ppmcs) {
                ppcMap.put(ppmc.getParkPointId(), ppmc.getMachineCount());
            }

            for (ParkPoint pp : listParkPoint) {
                Integer machineCount = ppcMap.get(pp.getParkPointId());
                if (machineCount != null) {
                    pp.setMachineCount(machineCount);
                } else {
                    pp.setMachineCount(0);
                }
            }
        }
        /** 校准 */
        calibrateService.celibrateParkPoint(listParkPoint, mapType);

        return JsonResult.succ(listParkPoint);
    }


    /**
     * 根据经纬度，获取附近还车点
     *
     * @param token
     * @param lon
     * @param lat
     * @param day
     * @param mapType
     * @param posFrom     位置来源（终端0，手机1 ，2融合定位包辅助还车（属于终端））
     * @param parkPointId
     * @param inside      0站外，1站内，null所有
     * @param returnType  还车点业务类型  实际0/投诉1/意向2
     * @param distance    附近多少米
     * @return
     */
    @RequestMapping("/getReturnPos")
    public JsonResult<List<ReturnBikeMsgLog>> getReturnPos(String token, Double lon, Double lat, Integer day, Integer accountId,
                                                           Integer mapType, Integer posFrom, Integer parkPointId, Integer inside, Integer returnType, Integer distance) {
        return JsonResult.succ(returnBikeMsgLogService.getPos(lon, lat, day, mapType, posFrom, parkPointId, inside, returnType, accountId, distance));
    }

    /**
     * @description: 设置站点投放的车辆数
     * @author: mo.shanyong
     * @date: 2025/4/29 15:47
     * @param: parkPoints
     * @param: accountId
     */
    private void setParkMachineCount(List<ParkPoint> parkPoints,Integer accountId){
        if (CollectionUtils.isEmpty(parkPoints)){
            return;
        }
        List<ParkPointMachineCount> ppmcs = parkPointService.groupByParkPoint(accountId);
        Map<Integer, Integer> ppcMap = new HashMap<Integer, Integer>();
        if (CollectionUtils.isNotEmpty(ppmcs)){
            ppcMap.putAll(ppmcs.stream().collect(Collectors.toMap(ParkPointMachineCount::getParkPointId,ParkPointMachineCount::getMachineCount)));
        }
        for (ParkPoint pp : parkPoints) {
            Integer machineCount = ppcMap.get(pp.getParkPointId());
            pp.setMachineCount(Objects.nonNull(machineCount) ? machineCount : 0);
        }

    }
}